Skip to content

fix(arborist): recognize registry-mediated tarballs - #9921

Open
martinrrm wants to merge 5 commits into
latestfrom
martinrrm/bug-npm-install-fails-with-eallowremote-on-certa
Open

fix(arborist): recognize registry-mediated tarballs#9921
martinrrm wants to merge 5 commits into
latestfrom
martinrrm/bug-npm-install-fails-with-eallowremote-on-certa

Conversation

@martinrrm

@martinrrm martinrrm commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • recognize registry-mediated tarballs served from same-origin sibling paths without trusting arbitrary lockfile URLs
  • preserve alias target identity and scoped authentication for ordinary peers, root-selected aliases shared with ordinary dependencies, and linked installs
  • validate locked versions and use cache-first registry evidence with coalesced abbreviated metadata requests
  • keep requested signature/attestation verification separate from URL-only evidence
  • document cold-cache and offline requirements

Problem

During reification, Arborist passes registry tarballs to pacote as name@URL. npm-package-arg parses that temporary spec as a remote dependency, so Arborist needs a narrow exemption for registry-mediated tarballs.

The existing classifier only recognized tarball paths beneath the configured registry path. Private registries can legitimately advertise tarballs from another path on the same origin. It also selected registries using the installed dependency slot: for hoek: npm:@npm/hoek@6.1.4, that loses the target scope and can select the wrong registry or credentials. Both cases can incorrectly fail with EALLOWREMOTE under the npm 12 default allow-remote=none.

Implementation

Package identity and compatibility

Identity comes from valid dependency specifications, not package/lockfile name fields. Aliases are unwrapped to target names. Ordinary peers constrain the installed slot rather than redefining an alias target. Project/workspace selections also take precedence over ordinary compatible transitive requirements, while disagreeing explicit alias targets remain rejected. A transitive alias cannot supersede a plain root selection.

Linked installs carry this identity through intermediate proxies and synthetic store nodes. Extraction uses the target identity independently of remote-permission mode, so authentication follows the target scope while the install location remains the alias slot.

Sibling-path verification

The configured-registry-path fast path remains. A same-origin URL outside that path is permitted only when registry evidence for the trusted package and exact version advertises the same effective tarball URL.

Locked versions must be exact semantic versions; aliases, ranges, tags, URLs, and other dependency specifications in that field cannot redirect verification. Metadata names and versions must match. Host replacement is applied consistently to both URLs. Fragments are ignored; the remaining URL, including path and query, must match.

Metadata reuse and request behavior

  • Capture compact URL evidence from actual pacote manifest responses during resolution, never from serialized node/lockfile fields.
  • Reuse full or abbreviated packuments already present in the memory cache.
  • Request abbreviated metadata on a verification cache miss and inspect the exact version entry without treating the locked version as a dependency spec.
  • Share pending verification requests per registry/package across instances and versions. Retain compact URL evidence even when the production cache discards large responses or responses without Content-Length.
  • Keep offline requests offline. An abbreviated ENOTCACHED result permits an offline lookup of the full HTTP variant, accounting for Vary: Accept.
  • Treat captured evidence as URL-only regardless of verification flags during resolution. Explicit signature/attestation verification still uses the dedicated full-metadata path; abbreviated old-lock hydration cannot bypass it.

Cold locked sibling-path installs can still require metadata requests. A cached tarball alone does not establish the additional evidence needed for an offline sibling-path install. This is an intentional availability/performance tradeoff, not a promise of zero additional requests.

Policy boundaries

Already-permitted implicit/explicit all, and root-direct requests under root, skip URL verification. Metadata failures, mismatches, unverifiable identities, and cross-origin URLs do not grant an exemption. Explicit remote dependencies remain governed by the configured policy. Extraction continues to receive the locked integrity value.

Regression coverage

  • malformed locked-version specs, including an alias injected into a wildcard lockfile entry, without unauthorized metadata or tarball requests
  • exact metadata name/version/URL matching, query mismatches, missing versions, and preserved semver build metadata
  • full/abbreviated memory caches, real offline HTTP variants, tarball-only caches, concurrent versions, and production-cache eviction conditions
  • alias credentials across permission modes; ordinary/optional peers; root-selected aliases with ordinary consumers; hoisted/linked installs; conflicting explicit selections
  • effective alias overrides, accepted version ranges, and both linked-node transformations
  • old-lock abbreviated metadata missing an attestation pointer present in full metadata, ensuring requested verification still runs and fails closed
  • npm ci tampered-lockfile regressions and updated configuration documentation

Fixes #9796

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martinrrm
martinrrm requested review from a team as code owners August 27, 2026 17:44
martinrrm and others added 3 commits August 27, 2026 18:01
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
})

t.test('allowRemote=none blocks same-origin tarball outside registry path', async t => {
t.test('allowRemote=none allows registry-advertised tarball outside registry path', async t => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test for the main change to fix #9796.

If a registry is serving tarballs from a different path than the actual registry metadata (such as https://registry.example.com/blob/package.tgz, while the registry is https://registry.example.com/npm) this results in EALLOWREMOTE attempting to install anything from it.

await t.resolves(arb.reify(), 'registry-advertised sibling-path tarball is allowed')
})

t.test('allowRemote=none verifies against dependency identity, not lockfile name', async t => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is also important; we are now getting the packument of the package (so a new metadata request if the tarball URL is different from the registry) and validating that the advertised URL are the same.

Validate locked versions, preserve root-selected aliases for ordinary consumers, and reuse registry URL evidence with coalesced abbreviated metadata requests. Keep explicit signature and attestation verification separate from captured URL evidence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] npm install fails with EALLOWREMOTE on certain private registry configurations

1 participant